מדריך מקיף לשיתוף משאבים בין מקורות (CORS), המכסה תצורה, השלכות אבטחה ושיטות עבודה מומלצות למפתחים.
שיתוף משאבים בין מקורות (CORS): תצורה ושיטות עבודה מומלצות לאבטחה
בעולם פיתוח הרשת, אבטחה היא ערך עליון. היבט קריטי אחד באבטחת רשת הוא ניהול האופן שבו דפי אינטרנט ממקור אחד יכולים לגשת למשאבים ממקור אחר. כאן נכנס לתמונה שיתוף משאבים בין מקורות (CORS). CORS הוא תכונת אבטחה בדפדפן המגבילה דפי אינטרנט מלבצע בקשות לדומיין שונה מזה שהגיש את דף האינטרנט. מנגנון זה קיים כדי למנוע מאתרים זדוניים לגשת לנתונים רגישים. מאמר זה מספק מדריך מקיף ל-CORS, המכסה את התצורה שלו, השלכות אבטחה ושיטות עבודה מומלצות.
הבנת מדיניות אותו מקור (Same-Origin Policy)
CORS בנוי על בסיס מדיניות אותו מקור (Same-Origin Policy), מנגנון אבטחה בסיסי המיושם על ידי דפדפני אינטרנט. מדיניות אותו מקור מגבילה דפי אינטרנט מלבצע בקשות לדומיין שונה מזה שהגיש את דף האינטרנט. שתי כתובות URL נחשבות כבעלות אותו מקור אם יש להן אותו פרוטוקול (למשל, HTTP או HTTPS), מארח (למשל, example.com), ופורט (למשל, 80 או 443). לדוגמה:
http://example.comו-http://example.com/pathהם מאותו מקור.http://example.comו-https://example.comהם ממקורות שונים (פרוטוקולים שונים).http://example.comו-http://www.example.comהם ממקורות שונים (מארחים שונים).http://example.com:80ו-http://example.com:8080הם ממקורות שונים (פורטים שונים).
מדיניות אותו מקור נועדה למנוע התקפות Cross-Site Scripting (XSS), שבהן אתר זדוני מזריק סקריפטים לאתר מהימן כדי לגנוב נתוני משתמש או לבצע פעולות לא מורשות. ללא מדיניות אותו מקור, אתר זדוני יכול היה לגשת לפרטי חשבון הבנק שלך אם היית מחובר לפורטל הבנקאות המקוונת שלך בלשונית אחרת.
מהו שיתוף משאבים בין מקורות (CORS)?
בעוד שמדיניות אותו מקור היא חיונית לאבטחה, היא יכולה גם להיות מגבילה בתרחישים לגיטימיים שבהם אתרים צריכים לגשת למשאבים ממקורות שונים. לדוגמה, יישום אינטרנט המתארח ב-example.com עשוי להצטרך לאחזר נתונים מ-API המתארח ב-api.example.net. CORS מספק מנגנון לעקוף את מדיניות אותו מקור באופן מבוקר, ומאפשר לדפי אינטרנט לבצע בקשות בין מקורות כאשר השרת מאשר זאת במפורש.
CORS פועל על ידי הוספת כותרות HTTP לתגובת השרת, המציינות אילו מקורות מורשים לגשת למשאב. הדפדפן בודק כותרות אלה וחוסם את הבקשה אם מקור דף האינטרנט המבצע את הבקשה אינו מורשה.
איך CORS עובד: כותרות ה-HTTP
CORS מסתמך על כותרות HTTP ספציפיות כדי לאפשר בקשות בין מקורות. הנה הכותרות המרכזיות המעורבות:
1. Origin (כותרת בקשה)
כותרת ה-Origin נשלחת על ידי הדפדפן בבקשות בין מקורות. היא מציינת את המקור (פרוטוקול, מארח ופורט) של דף האינטרנט המבצע את הבקשה. לדוגמה:
Origin: http://example.com
2. Access-Control-Allow-Origin (כותרת תגובה)
כותרת ה-Access-Control-Allow-Origin היא הכותרת החשובה ביותר ב-CORS. היא מציינת אילו מקורות מורשים לגשת למשאב. היא יכולה לקבל אחד מהערכים הבאים:
- מקור ספציפי: לדוגמה,
Access-Control-Allow-Origin: http://example.comמאפשר רק בקשות מ-http://example.com. *(תו כללי):Access-Control-Allow-Origin: *מאפשר בקשות מכל מקור. יש להשתמש בזה בזהירות, מכיוון שזה למעשה מבטל את מדיניות אותו מקור עבור אותו משאב.
דוגמה:
Access-Control-Allow-Origin: https://www.example.com
3. Access-Control-Allow-Methods (כותרת תגובה)
כותרת ה-Access-Control-Allow-Methods מציינת את מתודות ה-HTTP (למשל, GET, POST, PUT, DELETE) המותרות בבקשה בין המקורות. זה נדרש עבור בקשות preflight (יוסבר בהמשך).
דוגמה:
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
4. Access-Control-Allow-Headers (כותרת תגובה)
כותרת ה-Access-Control-Allow-Headers מציינת את כותרות ה-HTTP המותרות בבקשה בין המקורות. גם זה נדרש עבור בקשות preflight.
דוגמה:
Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With
5. Access-Control-Allow-Credentials (כותרת תגובה)
כותרת ה-Access-Control-Allow-Credentials מציינת אם הדפדפן צריך לכלול אישורים (למשל, עוגיות, כותרות אימות) בבקשה בין המקורות. היא יכולה לקבל אחד משני ערכים: true או false. אם מוגדר true, כותרת ה-Access-Control-Allow-Origin לא יכולה להיות מוגדרת ל-*. היא חייבת להיות מקור ספציפי.
דוגמה:
Access-Control-Allow-Credentials: true
6. Access-Control-Max-Age (כותרת תגובה)
כותרת ה-Access-Control-Max-Age מציינת את מספר השניות שהדפדפן יכול לשמור במטמון (cache) את תוצאות בקשת ה-preflight. זה יכול לשפר ביצועים על ידי הפחתת מספר בקשות ה-preflight.
דוגמה:
Access-Control-Max-Age: 3600
בקשות פשוטות לעומת בקשות Preflight
CORS מבחין בין שני סוגים של בקשות בין מקורות: בקשות פשוטות ובקשות preflight.
בקשות פשוטות
בקשה פשוטה היא בקשה העומדת בקריטריונים הבאים:
- המתודה היא
GET,HEAD, אוPOST. - אם המתודה היא
POST, כותרת ה-Content-Typeהיא אחת מהבאות:application/x-www-form-urlencoded,multipart/form-data, אוtext/plain. - הבקשה אינה מגדירה כותרות מותאמות אישית (פרט לאלו המוגדרות אוטומטית על ידי הדפדפן).
עבור בקשות פשוטות, הדפדפן שולח את הבקשה ישירות לשרת. השרת מגיב עם כותרות ה-CORS המתאימות. אם המקור מורשה, הדפדפן מעבד את התגובה. אחרת, הדפדפן חוסם את התגובה וזורק שגיאה.
בקשות Preflight
בקשת preflight נשלחת על ידי הדפדפן לפני ביצוע הבקשה הממשית בין המקורות אם הבקשה אינה עומדת בקריטריונים של בקשה פשוטה. זה קורה בדרך כלל כאשר הבקשה משתמשת במתודה שאינה GET, HEAD, או POST, או כאשר הבקשה מגדירה כותרות מותאמות אישית.
בקשת ה-preflight היא בקשת OPTIONS הכוללת את הכותרות הבאות:
Origin: מקור דף האינטרנט המבצע את הבקשה.Access-Control-Request-Method: מתודת ה-HTTP שתשמש בבקשה הממשית.Access-Control-Request-Headers: רשימה מופרדת בפסיקים של הכותרות המותאמות אישית שישמשו בבקשה הממשית.
השרת מגיב עם הכותרות הבאות:
Access-Control-Allow-Origin: המקור המורשה לגשת למשאב.Access-Control-Allow-Methods: מתודות ה-HTTP המותרות בבקשה בין המקורות.Access-Control-Allow-Headers: כותרות ה-HTTP המותרות בבקשה בין המקורות.Access-Control-Max-Age: מספר השניות שהדפדפן יכול לשמור במטמון את תוצאות בקשת ה-preflight.
אם השרת מגיב עם כותרות ה-CORS המתאימות, הדפדפן ממשיך עם הבקשה הממשית בין המקורות. אחרת, הדפדפן חוסם את הבקשה וזורק שגיאה.
דוגמאות לתצורת CORS
היישום של CORS משתנה בהתאם לטכנולוגיית צד השרת שבה אתה משתמש. הנה כמה דוגמאות לשפות וספריות צד שרת נפוצות:
Node.js עם Express
שימוש ב-middleware של cors הוא גישה נפוצה להגדרת CORS ב-Node.js עם Express:
const express = require('express');
const cors = require('cors');
const app = express();
// Enable CORS for all origins
app.use(cors());
// Enable CORS for a specific origin
// app.use(cors({ origin: 'http://example.com' }));
// Enable CORS with options
// app.use(cors({
// origin: ['http://example.com', 'http://localhost:3000'],
// methods: ['GET', 'POST', 'PUT', 'DELETE'],
// allowedHeaders: ['Content-Type', 'Authorization'],
// credentials: true
// }));
app.get('/api/data', (req, res) => {
res.json({ message: 'Hello from the API!' });
});
app.listen(3001, () => {
console.log('Server listening on port 3001');
});
פייתון עם Flask
ניתן להשתמש בתוסף Flask-CORS כדי להגדיר CORS ב-Flask:
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
# Enable CORS for all origins
CORS(app)
# Enable CORS for specific origins
# CORS(app, origins=['http://example.com', 'http://localhost:3000'])
@app.route('/api/data')
def get_data():
return {'message': 'Hello from the API!'}
if __name__ == '__main__':
app.run(port=3001)
Java עם Spring Boot
Spring Boot מספק מספר דרכים להגדיר CORS. גישה אחת היא להשתמש באנוטציה @CrossOrigin:
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin(origins = "http://example.com") // Specific origin
public class ApiController {
@GetMapping("/api/data")
public String getData() {
return "Hello from the API!";
}
}
// Global CORS configuration (using WebMvcConfigurer):
// @Configuration
// public class CorsConfig implements WebMvcConfigurer {
// @Override
// public void addCorsMappings(CorsRegistry registry) {
// registry.addMapping("/**")
// .allowedOrigins("http://example.com", "http://localhost:3000")
// .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
// .allowedHeaders("Content-Type", "Authorization")
// .allowCredentials(true)
// .maxAge(3600);
// }
// }
PHP
ב-PHP, ניתן להגדיר את כותרות ה-CORS ישירות בסקריפט:
<?php
header("Access-Control-Allow-Origin: http://example.com");
header("Content-Type: application/json");
$data = array("message" => "Hello from the API!");
echo json_encode($data);
?>
שיקולי אבטחה ב-CORS
בעוד ש-CORS מאפשר בקשות בין מקורות, חיוני להבין את השלכות האבטחה וליישם אותו נכון כדי למנוע פרצות.
1. הימנע משימוש ב-Access-Control-Allow-Origin: * בסביבת ייצור (Production)
שימוש בתו הכללי * בכותרת Access-Control-Allow-Origin מאפשר בקשות מכל מקור, ולמעשה מבטל את מדיניות אותו מקור עבור אותו משאב. זה יכול לחשוף את ה-API שלך לאתרים זדוניים שעלולים לגנוב נתוני משתמש או לבצע פעולות לא מורשות. במקום זאת, ציין את המקורות המדויקים המורשים לגשת למשאב. לדוגמה, אם יישום האינטרנט שלך מתארח ב-example.com וצריך לגשת ל-API המתארח ב-api.example.com, הגדר את הכותרת ל-Access-Control-Allow-Origin: http://example.com.
דוגמה גלובלית: דמיינו ש-API של שירות פיננסי מגדיר Access-Control-Allow-Origin: *. אתר זדוני יוכל אז לבצע בקשות ל-API זה בשם משתמש מחובר, ועלול להעביר כספים ללא ידיעת המשתמש.
2. ודא את תקינות כותרת ה-Origin בשרת
גם אם אתה מציין רשימה של מקורות מורשים, חשוב לוודא את תקינות כותרת ה-Origin בשרת כדי למנוע מתוקפים לזייף את המקור. תוקף עלול לשלוח בקשה עם כותרת Origin מזויפת כדי לעקוף את בדיקות ה-CORS. כדי למנוע זאת, השווה את כותרת ה-Origin עם רשימת מקורות מהימנים בצד השרת. אם המקור אינו ברשימה, דחה את הבקשה.
דוגמה גלובלית: קחו לדוגמה פלטפורמת מסחר אלקטרוני. תוקף יכול לנסות לחקות Origin של חנות לגיטימית כדי לגשת לנתוני לקוחות רגישים מה-API של פלטפורמת המסחר.
3. היזהר עם Access-Control-Allow-Credentials: true
אם אתה מגדיר Access-Control-Allow-Credentials: true, כותרת ה-Access-Control-Allow-Origin לא יכולה להיות מוגדרת ל-*. היא חייבת להיות מקור ספציפי. הסיבה לכך היא שמתן הרשאה לאישורים מכל מקור יכול ליצור סיכון אבטחתי, כיוון שהוא עלול לאפשר לאתרים זדוניים לגשת לנתוני משתמש אם הם יצליחו לגרום למשתמש לבקר באתרם בזמן שהוא מחובר גם לאתר היעד. הגדרה זו חשובה כאשר עוסקים בעוגיות או בכותרות אימות.
דוגמה גלובלית: פלטפורמת מדיה חברתית המאפשרת בקשות בין מקורות עם אישורים דורשת ניהול קפדני למניעת גישה לא מורשית לחשבונות משתמשים.
4. הגדר נכון את Access-Control-Allow-Methods ו-Access-Control-Allow-Headers
אפשר רק את מתודות וכותרות ה-HTTP הנחוצות לבקשות בין המקורות. אם אתה צריך לאפשר רק בקשות GET ו-POST, אל תאפשר PUT, DELETE, או מתודות אחרות. באופן דומה, אפשר רק את הכותרות הספציפיות שהיישום שלך צריך. תצורות מתירניות מדי עלולות להגביר את הסיכון להתקפות.
דוגמה גלובלית: מערכת CRM צריכה לחשוף רק את נקודות הקצה והכותרות הנחוצות של ה-API לאינטגרציות צד שלישי מורשות, ובכך למזער את שטח התקיפה.
5. השתמש ב-HTTPS לתקשורת מאובטחת
השתמש תמיד ב-HTTPS לתקשורת מאובטחת בין הדפדפן לשרת. HTTPS מצפין את הנתונים המועברים בין הדפדפן לשרת, ומונע האזנות והתקפות 'אדם בתווך' (man-in-the-middle). שימוש ב-HTTP עלול לחשוף נתונים רגישים לתוקפים, גם אם CORS מוגדר נכון.
דוגמה גלובלית: יישומי בריאות חייבים להשתמש ב-HTTPS כדי להגן על נתוני מטופלים המועברים בין מקורות שונים.
6. מדיניות אבטחת תוכן (CSP)
אף על פי שאינה קשורה ישירות ל-CORS, מדיניות אבטחת תוכן (CSP) היא מנגנון אבטחה חשוב נוסף שיכול לסייע במניעת התקפות XSS. CSP מאפשרת לך להגדיר רשימה לבנה (whitelist) של מקורות שהדפדפן מורשה לטעון מהם משאבים. זה יכול לעזור למנוע מתוקפים להזריק סקריפטים זדוניים לאתר שלך, גם אם הם מצליחים לעקוף אמצעי אבטחה אחרים.
דוגמה גלובלית: מוסדות פיננסיים לעיתים קרובות מיישמים מדיניות CSP קפדנית כדי להגביל את מקורות התוכן הנטענים לפורטלי הבנקאות המקוונת שלהם, ובכך מפחיתים את הסיכון להתקפות XSS.
בעיות נפוצות ב-CORS ופתרונן
שגיאות CORS יכולות להיות מתסכלות לניפוי באגים. הנה כמה בעיות נפוצות וכיצד לפתור אותן:
1. "No 'Access-Control-Allow-Origin' header is present on the requested resource."
זוהי שגיאת ה-CORS הנפוצה ביותר. היא מציינת שהשרת אינו מחזיר את כותרת ה-Access-Control-Allow-Origin בתגובתו. ודא שהשרת מוגדר לשלוח את כותרות ה-CORS הנכונות עבור מקור דף האינטרנט המבצע את הבקשה. בדוק שוב את קוד צד השרת וקבצי התצורה שלך.
2. "Response to preflight request doesn't pass access control check: It does not have HTTP ok status."
שגיאה זו מציינת שבקשת ה-preflight נכשלה. זה יכול לקרות אם השרת אינו מגיב לבקשות OPTIONS או אם השרת מחזיר קוד סטטוס של שגיאה (למשל, 404, 500) בתגובה לבקשת ה-preflight. ודא שהשרת שלך מוגדר לטפל בבקשות OPTIONS ושהוא מחזיר קוד סטטוס 200 OK.
3. "Response to preflight request doesn't pass access control check: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'."
שגיאה זו מתרחשת כאשר אתה מנסה לשלוח אישורים (למשל, עוגיות) בבקשה בין מקורות וכותרת ה-Access-Control-Allow-Origin מוגדרת ל-*. כפי שצוין קודם, אינך יכול להשתמש בתו הכללי * בעת שליחת אישורים. עליך לציין את המקור המדויק המורשה לגשת למשאב.
4. שמירה במטמון הדפדפן (Caching)
דפדפנים יכולים לשמור במטמון תגובות CORS, מה שעלול להוביל להתנהגות בלתי צפויה אם תצורת ה-CORS משתנה. כדי למנוע בעיות מטמון, הגדר את כותרת ה-Cache-Control בתגובה ל-no-cache, no-store, או max-age=0. ניתן גם להשתמש בכותרת Access-Control-Max-Age כדי לשלוט בכמה זמן הדפדפן שומר במטמון את תוצאות בקשת ה-preflight.
חלופות ל-CORS
בעוד ש-CORS היא הדרך הסטנדרטית לאפשר בקשות בין מקורות, ישנן כמה חלופות שתוכל לשקול בתרחישים מסוימים:
1. JSON עם ריפוד (JSONP)
JSONP היא טכניקה המשתמשת בתגית <script> כדי לעקוף את מדיניות אותו מקור. JSONP פועל על ידי עטיפת נתוני ה-JSON בקריאה לפונקציית JavaScript. הדפדפן מבצע את פונקציית ה-JavaScript, ומעביר את נתוני ה-JSON כארגומנט. JSONP פשוט יותר ליישום מ-CORS, אך יש לו כמה מגבלות. הוא תומך רק בבקשות GET, והוא פחות מאובטח מ-CORS.
2. פרוקסי הפוך (Reverse Proxy)
פרוקסי הפוך הוא שרת היושב לפני שרת ה-API שלך ומעביר אליו בקשות. ניתן להגדיר את הפרוקסי ההפוך כך שיוסיף את כותרות ה-CORS הנחוצות לתגובה, ובכך יסתיר את הבקשות בין המקורות מהדפדפן. גישה זו יכולה להיות שימושית אם אין לך שליטה על שרת ה-API או אם ברצונך לפשט את תצורת ה-CORS.
סיכום
שיתוף משאבים בין מקורות (CORS) הוא מנגנון אבטחה חיוני המאפשר לדפי אינטרנט לגשת למשאבים ממקורות שונים באופן מבוקר. הבנת אופן פעולת CORS ויישומו הנכון חיוניים לבניית יישומי אינטרנט מאובטחים ואמינים. על ידי ביצוע שיטות העבודה המומלצות המפורטות במאמר זה, תוכל לנהל ביעילות את CORS ולהגן על ממשקי ה-API שלך מפני גישה לא מורשית.
זכור תמיד לתת עדיפות לאבטחה בעת הגדרת CORS. הימנע משימוש בתווים כלליים, ודא את תקינות כותרת ה-Origin, והשתמש ב-HTTPS לתקשורת מאובטחת. על ידי נקיטת אמצעי זהירות אלה, תוכל להבטיח שיישומי האינטרנט שלך מוגנים מפני התקפות חוצות-אתרים.
מדריך מקיף זה מספק בסיס איתן להבנת CORS. עיין תמיד בתיעוד הרשמי של טכנולוגיית צד השרת הספציפית שלך לקבלת המידע המעודכן ביותר ושיטות העבודה המומלצות. הישאר מעודכן לגבי איומי אבטחה מתעוררים והתאם את תצורת ה-CORS שלך בהתאם.